/**
 * Copyright (c) 2016-2023, Michael Yang 杨福海 (fuhai999@gmail.com).
 * <p>
 * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.gnu.org/licenses/lgpl-3.0.txt
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.jpress.web.wechat;

import com.jfinal.log.Log;
import com.jfinal.weixin.sdk.api.AccessTokenApi;
import com.jfinal.weixin.sdk.api.ApiResult;
import com.jfinal.weixin.sdk.api.CustomServiceApi;
import com.jfinal.weixin.sdk.api.MediaApi;
import com.jfinal.weixin.sdk.utils.HttpUtils;
import com.jfinal.weixin.sdk.utils.JsonUtils;
import io.jboot.Jboot;
import io.jboot.components.cache.CacheTime;
import io.jboot.components.cache.JbootCache;
import io.jboot.utils.FileUtil;
import io.jboot.utils.NamedThreadPools;
import io.jboot.utils.QuietlyUtil;
import io.jboot.utils.StrUtil;
import io.jboot.web.attachment.AttachmentManager;
import io.jpress.model.WechatReply;

import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;


public class WechatMsgUtil {

    private static final Log LOG = Log.getLog(WechatMsgUtil.class);
    private static final Map<Integer, ExecutorService> POOLS = new HashMap<>();
    private static JbootCache mediaCache = Jboot.getCache();

    static {
        for (int i = 0; i <= 9; i++) {
            POOLS.put(i, NamedThreadPools.newFixedThreadPool(1, "wechat-msg" + i));
        }
    }

    private static ExecutorService getPool(String openId) {
        Integer key = Math.abs(openId.hashCode() % 10);
        return POOLS.get(key);
    }

    public static void sendMediaAsync(String openId, String mediaFilePath) {
        sendMediaAsync(openId, mediaFilePath, null);
    }

    public static void sendMediaAsync(String openId, String mediaFilePath, WechatReply wechatReply) {
        getPool(openId).submit(() -> {
            sendMedia(openId, mediaFilePath, wechatReply);
            QuietlyUtil.sleepQuietly(1000L);
        });
    }


    public static void sendTextAsync(String openId, String text) {
        getPool(openId).submit(() -> {
            sendText(openId, text);
            QuietlyUtil.sleepQuietly(1000L);
        });
    }


    public static boolean sendText(String openId, String text) {
        if (StrUtil.isBlank(text)) {
            return false;
        }
        text = StrUtil.unEscapeHtml(text);
        ApiResult result = CustomServiceApi.sendText(openId, text);
        return result.isSucceed();
    }


    public static boolean sendMedia(String openId, String filePath, WechatReply wechatReply) {

        final String suffix = FileUtil.getSuffix(filePath);
        if (StrUtil.isBlank(suffix)) {
            LOG.error("sendMedia error, can not get file suffix!!! file path: {}", filePath);
            return false;
        }


        String mediaId = uploadOrGetMediaId(filePath);
        if (mediaId == null) {
            return false;
        }


        ApiResult result = null;
        if (equalsAny(suffix, ".jpg", ".jpeg", ".png", ".gif")) {
            result = CustomServiceApi.sendImage(openId, mediaId);
        } else if (equalsAny(suffix, ".mp4") && wechatReply != null) {
            result = CustomServiceApi.sendVideo(openId, mediaId, wechatReply.getVideoTitle(), wechatReply.getVideoDesc());
        }

        if (result == null || !result.isSucceed()) {
            LOG.error("CustomServiceApi.media() is error : " + filePath + " \n" + result);
            return false;
        }

        return true;
    }

    private static boolean equalsAny(String str, String... anys) {
        for (String any : anys) {
            if (any != null && any.equalsIgnoreCase(str)) {
                return true;
            }
        }
        return false;
    }


    private static String customMessageUrl = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=";


    public static void sendMiniprogramAsync(String openId,
                                            String title,
                                            String appid,
                                            String pagepath,
                                            String imagePath) {
        getPool(openId).submit(() -> {
            sendMiniprogram(openId, title, appid, pagepath, imagePath);
            QuietlyUtil.sleepQuietly(1000L);
        });

    }

    public static ApiResult sendMiniprogram(String openId,
                                            String title,
                                            String appid,
                                            String pagepath,
                                            String imagePath) {


        String mediaId = uploadOrGetMediaId(imagePath);
        if (mediaId == null) {
            LOG.error("can not get media id for local path: {}", imagePath);
            return null;
        }


        Map<String, Object> jsonMap = new HashMap<>();
        jsonMap.put("touser", openId);
        jsonMap.put("msgtype", "miniprogrampage");


        Map<String, Object> miniprogrampageMap = new HashMap<>();
        miniprogrampageMap.put("title", title);
        miniprogrampageMap.put("appid", appid);
        miniprogrampageMap.put("pagepath", pagepath);
        miniprogrampageMap.put("thumb_media_id", mediaId);

        jsonMap.put("miniprogrampage", miniprogrampageMap);

        String accessToken = AccessTokenApi.getAccessTokenStr();
        String jsonResult = HttpUtils.post(customMessageUrl + accessToken, JsonUtils.toJson(jsonMap));

        final ApiResult apiResult = new ApiResult(jsonResult);

        if (!apiResult.isSucceed()) {
            LOG.error("发送小程序失败，错误信息：{}" + apiResult);
        }

        return apiResult;
    }


    public static String uploadOrGetMediaId(String mediaPath) {

        if (StrUtil.isBlank(mediaPath)) {
            return null;
        }

        final String suffix = FileUtil.getSuffix(mediaPath);
        if (StrUtil.isBlank(suffix)) {
            return null;
        }

        //处理 url 类型
        if (mediaPath.toLowerCase().startsWith("http://")) {
            mediaPath = mediaPath.substring(7);
            mediaPath = mediaPath.substring(mediaPath.indexOf("/"));
        } else if (mediaPath.toLowerCase().startsWith("https://")) {
            mediaPath = mediaPath.substring(8);
            mediaPath = mediaPath.substring(mediaPath.indexOf("/"));
        }


        String mediaId = mediaCache.get("wechatMedias", mediaPath);

        //去缓存里获取 mediaId
        if (StrUtil.isBlank(mediaId)) {

            File mediaFile = AttachmentManager.me().getFile(mediaPath);

            if (mediaFile == null || !mediaFile.exists() || !mediaFile.isFile() || !mediaFile.canRead()) {
                LOG.error("can not upload media to wechat server. media file not exisit or can not read" +
                        ", media file: {}", mediaFile);
                return null;
            }

            ApiResult apiResult = null;
            if (equalsAny(suffix, ".jpg", ".jpeg", ".png", ".gif")) {
                apiResult = MediaApi.uploadMedia(MediaApi.MediaType.IMAGE, mediaFile);
            } else if (equalsAny(suffix, ".mp4")) {
                apiResult = MediaApi.uploadMedia(MediaApi.MediaType.VIDEO, mediaFile);
            }


            /**
             * 上传临时素材
             * {"type":"TYPE","media_id":"MEDIA_ID","created_at":123456789}
             */
            if (apiResult == null || !apiResult.isSucceed()) {
                LOG.error("MediaApi.uploadMedia fail!!! " + mediaFile + " \n" + apiResult);
                return null;
            }

            mediaId = apiResult.get("media_id");

            //保存2天，官方默认是3天后 media_id 失效
            mediaCache.put("wechatMedias", mediaPath, mediaId, CacheTime.DAY * 2);
        }

        return mediaId;
    }
}
